package my.spring.db;

import my.project.util.Log;
import my.spring.annotation.Entity;
import my.spring.annotation.Query;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Dao接口的代理实现
 */
public class DaoProxy implements InvocationHandler {
    private final Class<?> target;

    public DaoProxy(Class<?> clazz) {
        this.target = clazz;
    }

    // 创建Dao接口的代理对象
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new DaoProxy(clazz));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 过滤基本方法，TODO 除了 toString 还有 equals 等等
        if ("toString".equals(method.getName())) {
            return target.getName();
        }
        Log.log("DaoProxy class={} method={}", target, method);
        String sql = getSql(method); // 执行查询的SQL语句
        Class<?> returnType = method.getReturnType(); // 执行查询后的返回类型
        if (sql.toUpperCase().startsWith("UPDATE ") || sql.toUpperCase().startsWith("INSERT ")) {
            return MySQL.executeUpdate(sql, args); // 执行UPDATE语句，返回影响的行数
        } else if (sql.toUpperCase().startsWith("SELECT ")) {
            List<Map<String, Object>> data = MySQL.executeQuery(sql, args); // 执行SELECT语句，返回数据集
            return parseData(method, returnType, data);
        } else {
            // TODO 其他类型语句暂不考虑
            return null;
        }
    }

    // 从Dao方法注解中得到SQL语句
    public String getSql(Method method) throws DbException {
        if (method.isAnnotationPresent(Query.class)) {
            Query q = method.getAnnotation(Query.class);
            return q.value().trim();
        }
        throw new DbException("必须在Dao方法上写上@query注解，方法：" + method);
    }

    // 结果集转目标对象
    public <T> T parseData(Method method, Class<T> clazz, List<Map<String, Object>> data) throws DbException {
        try {
            if (data != null && !data.isEmpty()) {
                if (clazz.isAnnotationPresent(Entity.class)) { // 返回类型是Entity
                    Map<String, Object> row = data.get(0);
                    return parseRow(row, clazz);
                } else if (clazz == java.util.List.class) { // 返回类型是List
                    // 获取List的泛型
                    Type type = method.getGenericReturnType();
                    if (type instanceof ParameterizedType) {
                        ParameterizedType pType = (ParameterizedType) type;
                        String typeName = pType.getActualTypeArguments()[0].getTypeName();
                        Class<?> typeClazz = Class.forName(typeName);
                        List list = new ArrayList<>();
                        for (Map<String, Object> row : data) {
                            Object obj = parseRow(row, typeClazz);
                            list.add(obj);
                        }
                        return (T) list;
                    }
                } else { // TODO 其他的类型暂不支持
                    throw new DbException("Dao方法中不支持此返回类型，" + clazz);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new DbException("数据库结果集转换失败，" + e.getMessage());
        }
        return null;
    }

    // 处理一行数据
    public <T> T parseRow(Map<String, Object> row, Class<T> clazz) throws InstantiationException, IllegalAccessException {
        T obj = clazz.newInstance();
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.isAccessible()) { // 是private的成员变量
                Object strValue = row.get(field.getName()); // TODO 这里还要考虑field的类型，分别处理
                if (strValue != null) {
                    field.setAccessible(true); // 打开权限
                    field.set(obj, strValue); // 设置进去
                }
            }
        }
        return obj;
    }
}
